#include <errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>

extern int tty_read(unsigned minor, char* buf, int count);
extern int tty_write(unsigned minor, char* buf, int count);

typedef int (*crw_ptr)(int rw, unsigned minor, char* buf, int count, off_t* pos, unsigned short flags);

static int rw_ttyx(int rw, unsigned minor, char* buf, int count, off_t *pos, unsigned short flags)
{
	return (rw == READ) ? tty_read(minor, buf, count) 
		: tty_write(minor, buf, count) ;
}

static int rw_tty(int rw, unsigned minor, char* buf, int count, off_t *pos, unsigned short flags)
{
	if (current->tty < 0)
		return -EPERM;
	return rw_tty(rw, current->tty, buf, count, NULL, 0);
}

#define NRDEVS	( sizeof(crw_table) / sizeof(crw_ptr) )

static crw_ptr crw_table[] = {
	NULL, 
	NULL, 
	NULL, 
	NULL, 
	rw_ttyx,
	rw_tty,
	NULL, 
	NULL
};

int char_read(struct inode *inode, struct file *filp, char *buf, int count)
{
    unsigned major, minor;
    crw_ptr call_addr;

    major = MAJOR(inode->i_data[0]);    // TODO: i_data 将使用 i_rdev 代替
    minor = MINOR(inode->i_data[0]);
    if (major >= NRDEVS)
        return -ENODEV;
    if (!(call_addr = crw_table[major]))
        return -ENODEV;
    return call_addr(READ, minor, buf, count, &filp->f_pos, filp->f_flags);
}

int char_write(struct inode * inode, struct file * filp, char * buf, int count)
{
	unsigned int major,minor;
	crw_ptr call_addr;

	major = MAJOR(inode->i_data[0]);
	minor = MINOR(inode->i_data[0]);
	if (major >= NRDEVS)
		return -ENODEV;
	if (!(call_addr=crw_table[major]))
		return -ENODEV;
	return call_addr(WRITE,minor,buf,count,&filp->f_pos,filp->f_flags);
}
